package model;

import java.util.ArrayList;
import java.util.Collections;

import roles.Reine;
import typesEnum.Evolution;
import typesEnum.Statut;
import etresVivant.EtreVivant;
import etresVivant.Fourmi;

/**
 * La vie de la fourmiliere tourne sur un thread.
 * Contient:
 * 	- liste des fourmis vivantes, mortes, et celles mises au depot de compost
 * 	- la Reine
 * 	- le stockage de nourriture 
 */
public class Fourmiliere {
	private final Case m_case;
	private ArrayList<Fourmi> 		fourmis;
	private ArrayList<Fourmi> 		cadavres;
	private ArrayList<EtreVivant> 	depot;
	protected Reine roleReine;
	protected Fourmi fourmiReine;
	protected float stockNourriture = 1000.0f; // milligrammes

	public Fourmiliere(Fourmi queen, Case c) {
		setRoleReine((Reine) queen.getRole());
		setFourmiReine(queen);
		m_case = c;
		fourmis = new ArrayList <Fourmi > (Collections.synchronizedList( new ArrayList< Fourmi >() ) );
		cadavres = new ArrayList<Fourmi>();
		depot = new ArrayList<EtreVivant>();
		fourmis.add(queen);
	}

	/* --- Getter & Setter --- */
	/**
	 * @return the cadavres
	 */
	public ArrayList<Fourmi> getCadavres() {
		return new ArrayList<Fourmi>(cadavres);
	}
	
	public Fourmi setCadavresRemove() {
		return cadavres.remove(0);
	}

	/**
	 * @return the reineFourmiliere
	 */
	public Reine getRoleReine() {
		return roleReine;
	}

	/**
	 * @param reineFourmiliere
	 *            the reineFourmiliere to set
	 */
	public void setRoleReine(Reine roleReine) {
		this.roleReine = roleReine;
	}

	/**
	 * @return the fourmiReine
	 */
	public Fourmi getFourmiReine() {
		return fourmiReine;
	}

	/**
	 * @param reineFourmiliere
	 *            the reineFourmiliere to set
	 */
	public void setFourmiReine(Fourmi fourmiReine) {
		this.fourmiReine = fourmiReine;
	}
	
	public void ajoutAuStock(float poids) {
		stockNourriture += poids;
	}

	/**
	 * @return liste "thread-safe" des fourmis
	 */
	public ArrayList<Fourmi> getFourmis() {
		return new ArrayList<Fourmi>( fourmis );
		// copie superficielle...
		// return (ArrayList<Fourmi>) fourmis.clone(); // renvoie un clone pour
		// continuer à écrire dedans
	}
	
	public void setFourmis( ArrayList<Fourmi> lesFourmis ) {
		fourmis.clear();
		fourmis.addAll(lesFourmis);
	}

	/**
	 * @return the m_case
	 */
	public Case getCase() {
		return m_case;
	}

	/**
	 * @return the depot
	 */
	public ArrayList<EtreVivant> getDepot() {
		return new ArrayList<EtreVivant>(depot);
	}
	
	public void setDepotAdd ( Fourmi f){
		depot.add(f);
	}
	
	
	
	/* --- VIE FOURMILIERE --- */
	/**
	 * Active la vie d'une fourmiliere
	 */
	public void activerVie() {
		new ThreadVie().start();
		System.out.println("...started a anthill thread");
	}
	
	/**
	 * Appelee depuis ThreadVie.
	 * 
	 * fonctionnement de la  fourmiliere
	 * avec gestion du temps, des dechets,
	 * des cycles de vie/mort,
	 * des actions des differentes entites,
	 * et demande de mise a jour au Terrain pour degrader les pheromones
	 */
	private void vivre() {
		int nbJours = 0;
		int jourAnnee, tous30Jours;
//		boolean mort = false;
		boolean bPhero = true;
		while (fourmis.size() != 0 || bPhero ) { // tant qu'il y a des fourmis
			if ( fourmis.size() == 0 ) {
				while ( bPhero = Terrain.degraderPheromones() ){					
				}
				break;
			}
//			try { Thread.sleep(50); } catch (InterruptedException e) {}
			
			jourAnnee = nbJours % 360;
			tous30Jours = nbJours % 30;
			// afficher dans la console tous les xxx jours
			if (tous30Jours == 0) {
				this.afficherPopulation(nbJours);
			}
			
			// La reine pond pendant le printemp.
			// Les 90 premiers jours
			if (fourmiReine.getStatut() == Statut.VIVANT && jourAnnee < 90) {
				fourmis.addAll(roleReine.pond(getCase()));
			}
			
			for (int nbHeure = 0; nbHeure < 24; nbHeure++){
//				for ( Proie p : Terrain.proies ) {
//					p.deplacer();
//				}
				
				// actions des fourmis
				for (Fourmi f : fourmis) {
					stockNourriture = f.seNourrir(stockNourriture);
					if (stockNourriture < 0) {
						stockNourriture =0;
					}
					boolean boolSexue = f.getRole().monRole().equals("Sexue");
					
					if (f.getEtat() == Evolution.ADULTE && !boolSexue){
						f.getRole().actions(this, f);
					}
					if (f.getEtat() == Evolution.ADULTE && boolSexue && jourAnnee >=90)
							f.getRole().actions(this, f);
				
					// ? morte
					if (f.getStatut() == Statut.NON_COMESTIBLE) {
						cadavres.add(f);
					}
				}
				fourmis.removeAll(cadavres);
			}
			
			for (Fourmi f : fourmis){
				f.vieillir();
				f.evoluer();
				Terrain.degraderPheromones();
			}
			decompositionDepot();
			nbJours++;
		}

		this.afficherPopulation(nbJours);
		Terrain.setAvailable(true);
		System.out.println("\n\n---> Fourmiliere detruite !");
	}
	
	/**
	 * Thread de la vie d'une Fourmiliere
	 */
	private class ThreadVie extends Thread {
		@Override
		public void run() {
			vivre();
		}
	}

	private void afficherPopulation(int nbJours) {
		int nbOeufs = 0, nbLarves = 0, nbNymphes = 0, nbFourmis = 0;
		int ouvrieres = 0, soldats = 0, sexues = 0;

		// pour chaque fourmi
		for (Fourmi f : fourmis) {
			Evolution etat = f.getEtat();
			switch (etat) {
			case OEUF:
				nbOeufs = nbOeufs + 1;
				break;
			case LARVE:
				nbLarves = nbLarves + 1;
				break;
			case NYMPHE:
				nbNymphes = nbNymphes + 1;
				break;
			case ADULTE:
				nbFourmis = nbFourmis + 1;
				ouvrieres += f.getRole().getOuvriere();
				soldats += f.getRole().getSoldat();
				sexues += f.getRole().getSexue();
			default:
				break;
			}
		}
		System.out.println("\n\n----- Population de la fourmiliere au "
				+ nbJours + " jours -----");
		System.out.println("Oeufs : " + nbOeufs);
		System.out.println("Larves : " + nbLarves);
		System.out.println("Lymphes : " + nbNymphes);
		System.out.println("Fourmis : " + nbFourmis);
		System.out.println("\t Ouvrieres : " + ouvrieres);
		System.out.println("\t Soldats : " + soldats);
		System.out.println("\t Sexues : " + sexues);
		System.out.println("Cadavres au dépot: " + depot.size());
		System.out.println("Cadavres dans la fourmiliere : " + cadavres.size());
		System.out.println("nourriture : " + stockNourriture/1000 + " g");
	}
	
	/**
	 * Vide progressivement ou totalement le depot de cadavres
	 */
	private void decompositionDepot (){
		depot.clear(); // vide entierement le depot 
		
		// solution realiste mais couteuse
//		int pourcent10 = depot.size() / 10;
//		if (depot.size() != 0)
//			depot.subList( 0, (pourcent10) ).clear();
	}

}
